﻿/* ****************************************************************************
 *
 * Copyright (c) Microsoft Corporation. 
 *
 * This source code is subject to terms and conditions of the Microsoft Public License. A
 * copy of the license can be found at http://go.microsoft.com/fwlink/?LinkID=131993. If
 * you cannot locate the Microsoft Public License, please send an email to
 * mixon@microsoft.com. By using this source code in any fashion, you are agreeing to
 * be bound by the terms of the Microsoft Public License.
 *
 * You must not remove this notice, or any other, from this software.
 *
 *
 * ***************************************************************************/

using System;
using System.Collections;
using System.Windows;
using System.Windows.Controls;

namespace MIXOnline.Descry
{
    /// <summary>
    /// This class determines what children should be showing on the VirtualizedTimelineStackPanel and the
    /// position of the scrollbar and then renders those children vertically. This control is designed to 
    /// be very efficient and can handle very large numbers of children without a huge amount of performance
    /// degredation when trying to animate it
    /// </summary>
    /// <note>
    /// The class manages adding and removing appropriate children from the UIElementCollectcion. it is written in
    /// a generic way to support adding any child that can be contained by a ContentPresenter.
    /// </note>
    public class VirtualizedTimelineStackPanel : Panel
    {

        private TimeWindow _timeWindow = new TimeWindow();

        public TimeWindow TimeWindow
        {
            get { return _timeWindow; }
            set { _timeWindow = value; }
        }

        public ITimelineGenerateData DataGenerator { get; set; }

        public VirtualizedTimelineStackPanel()
            : base()
        {
            VerticalAlignment = VerticalAlignment.Top; // align all children on top
            Orientation = Orientation.Vertical; // this control currently only supports vertical stacking.
        }

        public Orientation Orientation
        {
            get { return (Orientation)GetValue(OrientationProperty); }
            set { SetValue(OrientationProperty, value); }
        }

        public static readonly DependencyProperty OrientationProperty =
            DependencyProperty.Register("Orientation", typeof(Orientation), typeof(VirtualizedTimelineStackPanel), null);

        protected override Size MeasureOverride(Size availableSize)
        {
            if (DataGenerator != null) // if the panel is databound
            {
                // only show items that are in the scrollable viewport
                var myHeight = 0d;

                var indexWithinOriginalList = this.DataGenerator.FirstVisibleItemIndex;
                var indexWithinStackPanel = 0;

                int key = this.GetHashCode();

                while (myHeight < availableSize.Height && indexWithinOriginalList < this.DataGenerator.TotalItemsCount[key])
                {
                    UIElement currentItemContainer = null;

                    // Add another container as a child
                    if (indexWithinStackPanel < this.Children.Count)
                    {
                        currentItemContainer = this.Children[indexWithinStackPanel]; // reuse container
                        var dataItem = (this.DataContext as IList)[indexWithinOriginalList];
                        this.DataGenerator.PrepareContainerForItemOverride(currentItemContainer, dataItem);
                    }
                    else
                    {
                        currentItemContainer = this.DataGenerator.GetContainerForItemOverride(); // create new container and add as child
                        var dataItem = (this.DataContext as IList)[indexWithinOriginalList];
                        this.DataGenerator.PrepareContainerForItemOverride(currentItemContainer, dataItem);
                        this.Children.Add(currentItemContainer);
                    }

                    indexWithinStackPanel++;
                    indexWithinOriginalList++;

                    // Measure
                    currentItemContainer.Measure(new Size(Double.PositiveInfinity, Double.PositiveInfinity));
                    myHeight += currentItemContainer.DesiredSize.Height;
                }

                // If there's a partial item being displayed, the viewport size of the ScrollBar should not account for it. 
                // If the viewport size included partial items, it would not be possible to scroll them into view.
                var lastFullyVisibleIndex = indexWithinStackPanel;
                if (myHeight > availableSize.Height)
                {
                    lastFullyVisibleIndex--;
                }
                this.DataGenerator.FullyVisibleItemCount = Math.Max(lastFullyVisibleIndex, this.DataGenerator.FullyVisibleItemCount);

                // Remove any containers that are not visible (either because they fall outside of the visible area or because not enough items exist
                // to fill the visible area).
                while (indexWithinStackPanel < this.Children.Count)
                {
                    var container = this.Children[indexWithinStackPanel] as FrameworkElement;

                    // TODO: It's better to clear the container before removing it because *if* it has subscribed events, it won't be garbaged collected
                    // when removed from the tree.
                    //this.DataGenerator.ClearContainerForItemOverride(container, container.DataContext);

                    this.Children.RemoveAt(indexWithinStackPanel);
                }

                // rearrange this controls child EntryItems withing the bounds of the VirtualizedTimelineStackPanel 
                // to accurately show the chronical position of each item on the Timeline
                this.DataGenerator.RepositionEntryItemsWithinTimeStackPanel(this as VirtualizedTimelineStackPanel);

                return new Size(availableSize.Width, myHeight);
            }

            return availableSize;
        }

        // Arrange children and return final size of panel
        protected override Size ArrangeOverride(Size finalSize)
        {

            // Arranges the remaining children vertically
            double yPosition = 0;
            double maxHeight = 0;
            for (int i = 0; i < Children.Count; i++)
            {
                double desiredHeight = Children[i].DesiredSize.Height;
                Children[i].Arrange(new Rect(0, yPosition, finalSize.Width, desiredHeight));
                yPosition += desiredHeight;
                maxHeight = Math.Max(yPosition, maxHeight);
            }
            // return new final size
            return new Size(finalSize.Width, maxHeight);
        }

    }
}
